using System;
using System.Reflection;

namespace TeamTasker.Utils
{
    /// <summary>
    /// Represents utils methods for invoking the specified member.
    /// </summary>
    public class ReflectionUtils
    {
        /// <summary>
        /// Calls property getter.
        /// </summary>
        /// <param name="obj">The Object on which to invoke the specified getter.</param>
        /// <param name="propName">property name</param>
        /// <returns>invoked property value</returns>
        public static object CallGetProperty(object obj, String propName)
        {
            Type objType;
            if (obj != null)
            {
                objType = obj.GetType();
                try
                {
                    return objType.InvokeMember(propName, BindingFlags.GetProperty, null, obj, null);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(
                        "Failed to get property '" + propName +
                        "' from object of '" + obj.GetType().FullName + "' type", ex);
                }
            }
            return null;
        }

        /// <summary>
        /// Calls property setter.
        /// </summary>
        /// <param name="obj">The Object on which to invoke the specified setter.</param>
        /// <param name="propName">property name</param>
        /// <param name="propValue">property value to set</param>
        public static void CallSetProperty(object obj, String propName, Object propValue)
        {
            Type objType;
            if (obj != null)
            {
                objType = obj.GetType();
                object[] args = new object[1];
                args[0] = propValue;
                try
                {
                    objType.InvokeMember(propName, BindingFlags.SetProperty, null, obj, args);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(
                        "Failed to set property '" + propName +
                        "' to object of '" + obj.GetType().FullName + "' type", ex);
                }
            }
        }

        /// <summary>
        /// Invokes the specified method without parameters.
        /// </summary>
        /// <param name="obj">The Object on which to invoke the specified method.</param>
        /// <param name="methodName">The name of the method to invoke.</param>
        /// <returns>result of method's invoke</returns>
        public static object CallMethod(object obj, String methodName)
        {
            return CallMethod(obj, methodName, new object[] { });
        }

        /// <summary>
        /// Invokes the specified method with single parameter.
        /// </summary>
        /// <param name="obj">The Object on which to invoke the specified method.</param>
        /// <param name="methodName">The name of the method to invoke.</param>
        /// <returns>result of method's invoke</returns>
        /// <param name="param1">argument to pass to the member to invoke</param>
        public static object CallMethod(object obj, String methodName, object param1)
        {
            return CallMethod(obj, methodName, new object[] { param1 });
        }

        /// <summary>
        /// Invokes the specified method with two parameters.
        /// </summary>
        /// <param name="obj">The Object on which to invoke the specified method.</param>
        /// <param name="methodName">The name of the method to invoke.</param>
        /// <returns>result of method's invoke</returns>
        /// <param name="param1">argument to pass to the member to invoke</param>
        /// <param name="param2">argument to pass to the member to invoke</param>
        public static object CallMethod(object obj, String methodName, object param1, object param2)
        {
            return CallMethod(obj, methodName, new object[] { param1, param2 });
        }

        /// <summary>
        /// Invokes the specified method with three parameters.
        /// </summary>
        /// <param name="obj">The Object on which to invoke the specified method.</param>
        /// <param name="methodName">The name of the method to invoke.</param>
        /// <returns>result of method's invoke</returns>
        /// <param name="param1">argument to pass to the member to invoke</param>
        /// <param name="param2">argument to pass to the member to invoke</param>
        /// <param name="param3">argument to pass to the member to invoke</param>
        public static object CallMethod(object obj, String methodName, object param1, object param2, object param3)
        {
            return CallMethod(obj, methodName, new object[] { param1, param2, param3 });
        }

        protected static object CallMethod(object obj, String methodName, object[] parameters)
        {
            Type objType;
            if (obj != null)
            {
                objType = obj.GetType();
                try
                {
                    return objType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, obj, parameters);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(
                        "Failed to call '" + methodName +
                        "' method on the object of '" + obj.GetType().FullName + "' type", ex);
                }
            }
            return null;
        }

    }
}